﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using WCBG.ToolsForUnity.Tools;
using System.IO;
using UnityEngine.Timeline;
//using System;
#if UNITY_EDITOR
using UnityEditor;
#endif
using DynamicShadowProjector;

public class FTimelineTool
{

    public const string TIMELINE_ALLDATAPATH = "game_timeline/timeline_data/timeline_newdata";  // timeline all info
    public const string TIMELINE_DATA_BASEPATH = "game_timeline/timeline_single_data/"; // timeline info
    public const string TIMELINE_PB_ASSET_BASEPATH = "game_timeline/timeline_asset/";  // playableAsset




    public static PlayableAsset LoadPlayableAsset(string timelineID)
    {
        //if (Application.isEditor)
        //{
        //    return Resources.Load<PlayableAsset>(TIMELINE_PB_ASSET_BASEPATH + timelineID);
        //}

        string path = TIMELINE_PB_ASSET_BASEPATH + timelineID;
        return Res.LoadObj<PlayableAsset>(path);
    }

    public static FTimelineInfo LoadTimelineInfo(string timelineID)
    {
        string path = TIMELINE_DATA_BASEPATH + "timelinedata_" + timelineID;
        //if (Application.isEditor)
        //{
        //    TextAsset ta = Resources.Load<TextAsset>(path);
        //    return ta == null ? null : JsonUtility.FromJson<FTimelineInfo>(ta.text);
        //}
        string str = Res.LoadTextAsset(path);
        return string.IsNullOrEmpty(str) ? null : JsonUtility.FromJson<FTimelineInfo>(str);
    }

    public static void SetPlayableDirector(PlayableDirector playableDirector, PlayableAsset playableAsset, FTimelineInfo data)
    {

        if (playableDirector == null || playableAsset == null)
            return;

        playableDirector.playableAsset = playableAsset;
        Dictionary<string, PlayableBinding> pbDict = new Dictionary<string, PlayableBinding>();

        IEnumerable<PlayableBinding> Bindings = playableAsset.outputs;

        foreach (PlayableBinding bindind in Bindings)
        {
            //Debug.Log("sourceBindingType" + ", sourceObject:" + bindind.sourceObject + ", streamName:" + bindind.streamName + ", streamType:" + bindind.streamType);
            pbDict.Add(bindind.streamName, bindind);
        }

        playableDirector.gameObject.AddComponentEx<Animator>();
        if (pbDict.ContainsKey("AnimationTrack1"))
            playableDirector.SetGenericBinding(pbDict["AnimationTrack1"].sourceObject, playableDirector.gameObject);

        Dictionary<string, GameObject> cacheTest = new Dictionary<string, GameObject>();


        // other
        foreach (var temp in data.trackOtherList)
        {
            GameObject obj = null;
            if (cacheTest.ContainsKey(temp.objName))
            {
                obj = cacheTest[temp.objName];
            }
            else
            {
                if (Application.isPlaying)
                {
                    obj = Res.LoadObj(FPath.Combine(temp.assetPath, temp.objName));
                }
                else
                {
                    obj = Res.LoadObjForResources(FPath.Combine(temp.assetPath, temp.objName));
                }
                cacheTest.Add(temp.objName, obj);
            }
            if (obj == null)
                Debug.LogError("Load obj error! name:" + temp.objName);
            obj.transform.SetParent(playableDirector.transform);
            obj.name = temp.objName;
            obj.AddComponentEx<Animator>();
            TLIdentifyHolder holder = obj.AddComponentEx<TLIdentifyHolder>();
            holder.SetAssetPath(temp.assetPathType, temp.assetPath);

            //if (temp.assetType != FTimelineTrackInfo.EAssetType.Prefab)
            //{
            //    if (pbDict.ContainsKey(temp.trackName))
            //    {
            //        Object sourceObj = pbDict[temp.trackName].sourceObject;
            //        //obj.AddComponent(Type.GetType(temp.className));
            //        BaseTrackHolder trackHolder = obj.GetComponent<BaseTrackHolder>();
            //        playableDirector.SetGenericBinding(sourceObj, trackHolder);

            //        trackHolder.Initialize(playableDirector.name);
            //    }
            //}
        }

        // parent gameobject
        foreach (FTimelineTrackInfo temp in data.trackParentList)
        {
            GameObject obj = null;
            if (cacheTest.ContainsKey(temp.objName))
            {
                obj = cacheTest[temp.objName];
            }
            else
            {
                if (Application.isPlaying)
                {
                    obj = Res.LoadObj(FPath.Combine(temp.assetPath, temp.objName));
                }
                else
                {
                    obj = Res.LoadObjForResources(FPath.Combine(temp.assetPath, temp.objName));
                }
                cacheTest.Add(temp.objName, obj);
            }

            if (obj == null)
                Debug.LogError("Load obj error! name:" + temp.objName);
            obj.transform.SetParent(playableDirector.transform);
            obj.name = temp.objName;
            obj.AddComponentEx<Animator>();
            TLIdentifyHolder holder = obj.AddComponentEx<TLIdentifyHolder>();
            holder.SetAssetPath(temp.assetPathType, temp.assetPath);


            if (pbDict.ContainsKey(temp.trackName))
            {
                if (temp.assetType == FTimelineTrackInfo.EAssetType.GameObject)
                {
                    Object sourceObj = pbDict[temp.trackName].sourceObject;
                    playableDirector.SetGenericBinding(sourceObj, obj);
                }
                else if (temp.assetType == FTimelineTrackInfo.EAssetType.BaseTrackHolder)
                {
                    Object sourceObj = pbDict[temp.trackName].sourceObject;
                    BaseTrackHolder trackHolder = obj.GetComponent<BaseTrackHolder>();
                    playableDirector.SetGenericBinding(sourceObj, trackHolder);
                    trackHolder.Initialize(playableDirector.name);
                }

            }
            //else
            //{
                //Debug.LogError("Get sourceObject falid!:trackname:" + temp.trackName);
            //}
        }

        // child 
        foreach (var temp in data.trackChildList)
        {
            if (cacheTest.ContainsKey(temp.parentName))
            {
                GameObject parentObj = cacheTest[temp.parentName];
                Transform childTarget = parentObj.transform.FindChildEx(temp.objName);
                if (childTarget == null)
                {
                    Debug.LogError("find child error! child name:" + temp.objName);
                    continue;
                }

                GameObject childObj = childTarget.gameObject;
                if (childObj != null)
                {
                    childObj.AddComponentEx<Animator>();

                    if (temp.assetType == FTimelineTrackInfo.EAssetType.GameObject)
                    {
                        playableDirector.SetGenericBinding(pbDict[temp.trackName].sourceObject, childObj);
                    }
                    else if (temp.assetType == FTimelineTrackInfo.EAssetType.BaseTrackHolder)
                    {
                        BaseTrackHolder trackHolder = childObj.GetComponent<BaseTrackHolder>();
                        if (trackHolder == null)
                        {
                            Debug.LogError("childobj get BaseTrackHolder error! name:" + childObj.name);
                        }
                        playableDirector.SetGenericBinding(pbDict[temp.trackName].sourceObject, trackHolder);
                        trackHolder.Initialize(playableDirector.name);
                    }
                }
            }
            else
            {
                Debug.Log("Parent is null:" + temp.parentName);
            }
        }

        // audio
        foreach (var temp in data.trackAudioList)
        {
            // 需要为音频轨道新建一个GameObject，不能使用角色对象或者已经绑定到其它轨道的对象，否则停止播放的时候会导致编辑器奔溃
            GameObject audioObj = new GameObject(temp.objName + " -- audio");
            audioObj.transform.SetParent(playableDirector.transform);
            playableDirector.SetGenericBinding(pbDict[temp.trackName].sourceObject, audioObj.AddComponentEx<AudioSource>());
            //if (cacheTest.ContainsKey(temp.objName))
            //{
            //    //GameObject audioObj = cacheTest[temp.objName];
            //    GameObject audioObj = new GameObject(temp.objName + " -- audio");
            //    audioObj.transform.SetParent(playableDirector.transform);
            //    playableDirector.SetGenericBinding(pbDict[temp.trackName].sourceObject, audioObj.AddComponentEx<AudioSource>());
            //}
            //else
            //{
            //    //GameObject audioObj = new GameObject(temp.objName);
            //    GameObject audioObj = new GameObject(temp.objName + " -- audio");
            //    audioObj.transform.SetParent(playableDirector.transform);
            //    playableDirector.SetGenericBinding(pbDict[temp.trackName].sourceObject, audioObj.AddComponentEx<AudioSource>());
            //}
        }


        // 设置阴影投射

        foreach (GameObject go in cacheTest.Values)
        {
            DrawTargetObject dt = go.GetComponent<DrawTargetObject>();
            if (dt != null)
            {
                if (dt.selectMode == DrawTargetObject.Select.None)
                {
                    dt.target = playableDirector.transform;
                }
                else
                {
                    string name = dt.GetActorName();
                    if (cacheTest.ContainsKey(name))
                    {
                        dt.target = cacheTest[name].transform;
                    }
                }
            }
        }


    }

    public static void SaveTimelineToInfo(PlayableDirector director, string id, string des)
    {
        if (director == null)
            return;
        if (CheckBindindName(director))
            return;

        TLIdentifyHolder[] arr = director.GetComponentsInChildren<TLIdentifyHolder>(true);
        List<TLIdentifyHolder> addedList = new List<TLIdentifyHolder>();
        addedList.AddRange(arr);

        FTimelineInfo tlInfo = new FTimelineInfo();
        if (director != null)
        {
            IEnumerable<PlayableBinding> Bindings = director.playableAsset.outputs;

            tlInfo.ID = id;
            tlInfo.describe = des;
            foreach (PlayableBinding bindind in Bindings)
            {
                Object obj = director.GetGenericBinding(bindind.sourceObject);
                if (obj != null)
                {
                    //Debug.Log("type:" + obj.GetType() + "  name:" + obj.name);

                    FTimelineTrackInfo trackInfo = new FTimelineTrackInfo();
                    trackInfo.trackName = bindind.streamName;
                    trackInfo.objName = obj.name;

                    GameObject gameObject = obj as GameObject;
                    if (gameObject != null)
                    {
                        trackInfo.assetType = FTimelineTrackInfo.EAssetType.GameObject;

                        TLIdentifyHolder identify = gameObject.GetComponent<TLIdentifyHolder>();
                        if (identify != null)
                        {
                            trackInfo.assetPath = identify.AssetPath;
                            trackInfo.assetPathType = (int)identify.pathType;

                            tlInfo.trackParentList.Add(trackInfo);
                            if (addedList.Contains(identify)) addedList.Remove(identify);
                        }
                        else
                        {
                            TLIdentifyHolder indentifyParent = gameObject.GetComponentInParentEx<TLIdentifyHolder>();
                            if (indentifyParent != null)
                            {
                                trackInfo.parentName = indentifyParent.name;
                                tlInfo.trackChildList.Add(trackInfo);
                            }
                            else
                            {
                                if (gameObject.transform.parent != null)
                                    Debug.LogError("Get parent TLIdentifyHolder error! name:" + gameObject.name);
                            }
                        }
                    }
                    if (obj.GetType() == typeof(AudioSource))
                    {
                        trackInfo.objName = obj.name;
                        trackInfo.assetType = FTimelineTrackInfo.EAssetType.AudioSource;
                        tlInfo.trackAudioList.Add(trackInfo);
                    }
                    //else if (obj.GetType() == typeof(EventReceiver))
                    //{
                    //    trackInfo.objName = obj.name;
                    //    trackInfo.assetType = FTimelineTrackInfo.EAssetType.EventReceiver;
                    //    tlInfo.trackOtherList.Add(trackInfo);
                    //}
                    else if (typeof(BaseTrackHolder).IsAssignableFrom(obj.GetType()))
                    {
                        trackInfo.objName = obj.name;
                        trackInfo.assetType = FTimelineTrackInfo.EAssetType.BaseTrackHolder;

                        BaseTrackHolder holder = obj as BaseTrackHolder;
                        TLIdentifyHolder identify = holder.gameObject.GetComponent<TLIdentifyHolder>();

                        if (identify != null)
                        {
                            trackInfo.assetPath = identify.AssetPath;
                            trackInfo.assetPathType = (int)identify.pathType;
                            tlInfo.trackParentList.Add(trackInfo);
                            if (addedList.Contains(identify)) addedList.Remove(identify);
                        }
                        else
                        {
                            TLIdentifyHolder indentifyParent = holder.gameObject.GetComponentInParentEx<TLIdentifyHolder>();
                            if (indentifyParent != null)
                            {
                                trackInfo.parentName = indentifyParent.name;
                                tlInfo.trackChildList.Add(trackInfo);
                            }
                            else
                            {
                                if (holder.transform.parent != null)
                                    Debug.LogError("Get parent TLIdentifyHolder error! name:" + holder.name);
                            }
                        }
                    }
                }
            }
        }
        foreach (TLIdentifyHolder identify in addedList)
        {
            FTimelineTrackInfo trackInfo = new FTimelineTrackInfo();
            trackInfo.assetType = FTimelineTrackInfo.EAssetType.Prefab;
            trackInfo.objName = identify.name;
            trackInfo.assetPath = identify.AssetPath;
            trackInfo.assetPathType = (int)identify.pathType;
            tlInfo.trackOtherList.Add(trackInfo);

            //Debug.LogError(identify.name);
        }

        string path = Application.dataPath + "/Resources/" + TIMELINE_DATA_BASEPATH;
        if (!FDirectory.Exists(path))
        {
            FDirectory.CreateDirectory(path);
        }
        string jsonPath = path + "timelinedata_" + id + ".json";
        File.WriteAllText(jsonPath, JsonUtility.ToJson(tlInfo));
        Debug.Log("保存timeline数据:" + jsonPath);

#if UNITY_EDITOR
        AssetDatabase.Refresh();
#endif
    }

    public static Dictionary<string, FTimelineInfo> LoadTimeLineDict()
    {
        Dictionary<string, FTimelineInfo> tlDict = new Dictionary<string, FTimelineInfo>();
        TextAsset textAsset = Resources.Load<TextAsset>(TIMELINE_ALLDATAPATH);

        if (textAsset != null)
        {
            FTimelineAllInfo tlData = JsonUtility.FromJson<FTimelineAllInfo>(textAsset.text);

            for (int i = 0; i < tlData.timelineAllInfoList.Count; i++)
            {
                FTimelineInfo singleData = tlData.timelineAllInfoList[i];
                tlDict.Add(singleData.ID, singleData);
            }
            return tlDict;
        }
        return null;
    }

    // 删除数据
    public static void DeleteTimelineInfo(string id)
    {
        string path = Application.dataPath + "/Resources/" + TIMELINE_DATA_BASEPATH + "timelinedata_" + id + ".json";
        if (File.Exists(path))
        {
            File.Delete(path);
            Debug.Log("删除timeline数据:" + path);
        }

#if UNITY_EDITOR
        AssetDatabase.Refresh();
#endif

    }

    // 删除数据
    public static void RenameAllTrack(string timelineID)
    {
        //string path = "Assets/Resources/game_cake/game_cake_timeline/Level_1_Timeline.playable";
#if UNITY_EDITOR
        TimelineAsset director = (TimelineAsset)AssetDatabase.LoadAssetAtPath("Assets/Resources/" + TIMELINE_PB_ASSET_BASEPATH + timelineID + ".playable", typeof(TimelineAsset));
        for (int i = 0; i < director.outputTrackCount; i++)
        {
            TrackAsset asset = director.GetOutputTrack(i);
            asset.name = "AnimationTrack" + (i + 1);
        }
        AssetDatabase.SaveAssets();
#endif
    }

    private static bool CheckBindindName(PlayableDirector director)
    {
        IEnumerable<PlayableBinding> Bindings = director.playableAsset.outputs;
        List<string> list = new List<string>();
        foreach (PlayableBinding binding in Bindings)
        {
            if (list.Contains(binding.streamName))
            {
                Debug.LogError("命名重复：" + binding.streamName);
                return true;
            }
            list.Add(binding.streamName);
        }
        return false;
    }

    public static void Test()
    {
#if UNITY_EDITOR
        TimelineAsset timelineAsset = (TimelineAsset)AssetDatabase.LoadAssetAtPath("Assets/Resources/" + TIMELINE_PB_ASSET_BASEPATH + "30002.playable", typeof(TimelineAsset));
        for (int i = 0; i < timelineAsset.outputTrackCount; i++)
        {
            TrackAsset asset = timelineAsset.GetOutputTrack(i);
            timelineAsset.DeleteTrack(asset);
        }

        ReceiverTrackAsset track = timelineAsset.CreateTrack<ReceiverTrackAsset>(null, "testTrack");
        TimelineClip clip = track.CreateDefaultClip();
        clip.asset  = ReceiverPlayableAsset.CreateInstance("ReceiverPlayableAsset");
        clip.start = 2;

        AnimationTrack track1 = timelineAsset.CreateTrack<AnimationTrack>(null, "ledi");
        TimelineClip clip1 = track1.CreateDefaultClip();
        clip1.asset = AnimationPlayableAsset.CreateInstance("AnimationPlayableAsset");
        AnimationPlayableAsset clipAsset = (AnimationPlayableAsset)clip1.asset;
        clipAsset.clip = (AnimationClip)AssetDatabase.LoadAssetAtPath("Assets/Models/201002000@idle01.FBX", typeof(AnimationClip));

        AssetDatabase.SaveAssets();
#endif
    }

}
